iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 6
2
AI & Data

30 天學會深度學習和 Tensorflow系列 第 6

05. 深度學習架構的主要成分介紹

  • 分享至 

  • xImage
  •  

一個深度學習架構通常包含了以下架構:

Typical Deep Learning System Stack

在上圖中,我們可以看到粗略分為屬於軟題相關的如,使用者 API(User API),執行系統單元(System components)和於硬體相關(computer architecture)。使用者 API,包含如何實作類神經網路運算元,學習演算法以及梯度數值運算的部分。在使用者 API 之上,則是包括了如 Keras 更為抽象的 API。

使用者 API 的實作方式,目前深度學習架構的趨勢則是利用計算圖的方法來作為資料結構。而計算圖的設計方式,則使深度學習的架構朝向兩個不同的方向發展:一個是以 PyTorch 為主的動態計算圖,另一個則是以 Tensorflow 為主的靜態計算圖。

說到這裡,讀者們難免疑惑,倒底什麼是計算圖,計算圖的設計,對於類神經網路的執行能力有什麼影響呢?

計算圖的發展,可以追朔到三十多年前的一個創新主意,那就是針對類神經網路或機械學習演算法所發展的自動梯度計算方式(Automatic Differentiation)。類神經網路的訓練,依賴兩個步驟:

  1. 第一個步驟是正向傳播(feed forward),就是將訓練資料餵送給類神經網路,藉著往上攀爬類神經網路內,層層疊起的網路結構,最後到達最上層的輸出神經元。

  2. 第二個步驟則是反向傳播(back propagation),將類神經網路最上層的輸出,與正確的分類標籤或數值代入損失函數後,計算出的誤差值,需要再次由上而下,對各層參數取偏微分計算,以計算該層參數對誤差的個別貢獻後,再往下回溯深不可測的層層堆疊網絡,透過連鎖規則(chain rule),更新每一層的權重和偏移參數值。

然而,正向或反向的傳播計算,所仰賴的是一個相當基礎的微分技巧,chain rule 來執行。而計算圖,則是將正向傳播的計算順序,以一個有方向的圖來表示。在計算圖中,每一個節點都是一個運算元,或代表資料的張量(tensor)。節點與節點之間,具有方向的連結,則是運算元執行的順序。下圖就是一個非常簡單的計算圖。

Computationl graph

一個常數張量的運算元節點,可以是最小的運算圖。許多小的計算圖,則可以透過相依的變數,構成一個巨大的運算圖。在這個由許多小計算圖構成的大計算圖裡,每一個在大計算圖內的子圖,都是一個完整的子運算式,根據與嵌入的大運算圖的連結關係,可以獨立成為一個模組,分配到不同的裝置,先行計算。

而根據計算圖如何在程式中執行,計算圖又可分為動態計算圖(dynamic computational graph)和靜態符號式計算圖(static/symbolic computational graph)。所謂的動態計算圖,也就是目前深度學習的趨勢,包括 PyTorch 和 Gluon 都採用此類的方式來建構計算圖。

在動態計算圖中,有圖的整個結構無需再一開始就必須建構好,透過追蹤程式的執行方式來動態執行,所以又被稱為執行期間的自動微分(runtime autodiff)。這樣的方式有利於直譯式語言,去動態執行計算圖中的每一個子圖中的運算,並及時得到運算的結果。

而靜態或又稱為符號式計算圖,則需要開發者在執行前先將所有的計算圖子圖都建構好,所以又被稱為事先自動微分(ahead-of-time audodiff),或根據 Google 官方部落格所宣稱的「動態資料流圖形」(dynamic data-flow graph)(顯然地,tensoflow 的核心開發者並不覺得他們的計算圖是靜態的),這樣的計算圖方式,有利於在編譯式語言上執行。

事實上,動態資料流計算圖(dynamic data-flow graph),在 1960 年代便提出,原先是使用在平行運算上的編程方法,是屬於程式設計法的範疇。該程式編寫與一般程式編寫不同的地方在於,一般程式編寫,或所謂的程序程式設計法(imperative programming),將中央處理器需要處理的命令步驟,按造執行的順序來編寫。然而這樣的編寫方式或許適合單一執行程序,以序列執行的方式執行,在多計算單元的運算平台上,編寫平行化程式時,編程者卻必須手動安排在不同單元上執行的時程,而造成執行效率地降低。若以動態資料流計算圖的編程法,則能讓程式編譯器(compiler)有效的找出一個運算元的輸入和輸出(相較於傳統程式設計法,需要將原始碼轉換成文法樹,syntax tree),而達到在異質化計算平台上平行執行的高速表現。

Heterogeneous computing

在實作上,tensorflow 真正的計算核心是和 python 直譯器分開來的,也因如此在真正執行的時候,需要一個 Session 的物件來與計算核心溝通,並在這個 Session 餵以計算核心資料,並透過 Session 來做除錯。然而,缺乏動態除錯的方式,造成了許多研究者,還在實驗性階段,嘗試不同的類神經網路模型,或最佳化演算法時,使用直譯式語言,如 Python,除錯上的困難,也因此失去了 Python 這種動態語言,在快速建立實驗性原型時的優點。

為了克服這項困難,Tensorflow 在 2017 年發表了 eager 模式執行的 API。在這個模式下,使用者可以在互動式的介面中,看到原本在 graph 模式(尚未餵以資料)時的輸入資料(以 numpy 陣列的方式),亦可以直接使用 python function 透過 tensorflow data API 對符號計算圖中的各個運算元做資料處理。

在 gradient 的計算上,新的 Tensorflow API 更提供了介面,可以讓使用者自行提供函示計算梯度,也可以僅提供函示,而由 Tensorflow 為你的函式產生 python 原始碼來計算梯度。任何高階的微分,都可以藉由多次呼叫計算梯度函式來達成。這個自動生成計算梯度的方式,是依照一個稱為 “tape” 的設計。

在此設計下,tensorflow 會記錄計算節點的執行順序,這個被記錄的執行順序稱為 “tape”。當要計算,使用者所提供的函式時,便會依照 tape 所記錄下的執行步驟,以反向的方式,在計算圖中拜訪每個計算元,並由每個計算元所提供的梯度計算方式,來依據 chain rule 進行總梯度計算。

最後用一個 Tensorflow 提供的簡單 tape 範例來做結尾。(tensorflow 的版本為 1.12)

import tensorflow as tf
w = tf.Variable([[1.0]])
with tf.GradientTape() as tape:
  loss = w * w
# 已經利用 tf.GradientTape 紀錄了 function 的計算方法
# 接下來讓 tape 依照計算圖來計算梯度。
grad = tape.gradient(loss, w)

上一篇
04. 鐵達尼預測內幕:避免過度學習的方法
下一篇
06. 深度學習的架構分析: 多層感知器
系列文
30 天學會深度學習和 Tensorflow30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言